using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Net;
using System.IO;
using System.Reflection;

namespace SPTraceViewUpdate
{
    internal class Updater
    {
        //private const string UPDATE_LOCATION = "http://www.hristopavlov.net/SPTraceView/";
        private const string UPDATE_LOCATION = "https://sptraceview.svn.codeplex.com/svn/SPTraceView2007/";
        private string m_InstallPath = null;

        public Updater(string installPath)
        {
            m_InstallPath = installPath;
        }

        public XmlNode NewUpdatesAvailable()
        {
            Uri updateUri = new Uri(UPDATE_LOCATION + "Updates.xml");

            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(updateUri);
            httpRequest.Method = "GET";
            httpRequest.Timeout = 30000; //30 sec

            HttpWebResponse response = null;

            try
            {
                response = (HttpWebResponse)httpRequest.GetResponse();

                string updateXml = null;

                Stream streamResponse = response.GetResponseStream();

                try
                {
                    using (TextReader reader = new StreamReader(streamResponse))
                    {
                        updateXml = reader.ReadToEnd();
                    }
                }
                finally
                {
                    streamResponse.Close();
                }

                if (updateXml != null)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(updateXml);

                    int latestVersion = CurrentlyInstalledVersion();
                    XmlNode latestVersionNode = null;

                    foreach (XmlNode updateNode in xmlDoc.SelectNodes("SPTraceView/Update"))
                    {
                        int Version = int.Parse(updateNode.Attributes["Version"].Value);
                        if (latestVersion < Version)
                        {
                            latestVersion = Version;
                            latestVersionNode = updateNode;
                        }
                    }

                    return latestVersionNode;
                }
            }
            finally
            {
                // Close response
                if (response != null)
                    response.Close();
            }

            return null;
        }

        public bool PrepareToUpdate()
        {
            return true;
        }

        public bool Update(XmlNode node)
        {
            if (node != null)
            {
                int fileId = 0;
                foreach (XmlNode fileNode in node.ChildNodes)
                {
                    if ("File".Equals(fileNode.Name))
                    {
                        string location = fileNode.Attributes["Path"].Value;
                        string localFile = Path.GetFullPath(m_InstallPath + "//" + Path.GetFileName(location));

                        Uri fileUri = null;

                        //MFD: we need to support both full and relative urls for back compatibility.
                        if (location.StartsWith("http"))
                            fileUri = new Uri(location);
                        else
                            fileUri = new Uri(UPDATE_LOCATION + location.TrimStart(new char[] { '/' }));

                        fileId++;

                        HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(fileUri);
                        httpRequest.Method = "GET";
                        httpRequest.Timeout = 1200000; //1200 sec = 20 min

                        HttpWebResponse response = null;

                        try
                        {
                            response = (HttpWebResponse)httpRequest.GetResponse();

                            Stream streamResponse = response.GetResponseStream();

                            try
                            {
                                bool allGood = false;
                                int attempts = 0;

                                bool isZipFile = ".zip".Equals(Path.GetExtension(localFile), StringComparison.InvariantCultureIgnoreCase);
                                if (isZipFile)
                                    localFile = Path.ChangeExtension(localFile, ".exe");

                                Exception fileDelException = null;
                                do
                                {
                                    try
                                    {
                                        if (File.Exists(localFile))
                                            File.Delete(localFile);

                                        allGood = true;
                                        fileDelException = null;
                                    }
                                    catch (Exception ex)
                                    {
                                        fileDelException = ex;
                                        System.Threading.Thread.Sleep(500);
                                    }

                                    attempts++;
                                }
                                while (!allGood && attempts < 10);

                                if (fileDelException != null)
                                {
                                    return false;
                                }

                                if (isZipFile)
                                {
                                    using (MemoryStream memStr = new MemoryStream())
                                    using (BinaryReader reader = new BinaryReader(streamResponse))
                                    using (BinaryWriter writer = new BinaryWriter(memStr))
                                    {
                                        byte[] chunk = null;
                                        do
                                        {
                                            chunk = reader.ReadBytes(1024);
                                            writer.Write(chunk);
                                        }
                                        while (chunk != null && chunk.Length == 1024);

                                        writer.Flush();

                                        Unzip.UnZip(memStr, m_InstallPath, false);
                                    }
                                }
                                else
                                {
                                    using (BinaryReader reader = new BinaryReader(streamResponse))
                                    using (BinaryWriter writer = new BinaryWriter(new FileStream(localFile, FileMode.Create)))
                                    {
                                        byte[] chunk = null;
                                        do
                                        {
                                            chunk = reader.ReadBytes(1024);
                                            writer.Write(chunk);
                                        }
                                        while (chunk != null && chunk.Length == 1024);

                                        writer.Flush();
                                    }
                                }
                            }
                            finally
                            {
                                streamResponse.Close();
                            }

                        }
                        finally
                        {
                            // Close response
                            if (response != null)
                                response.Close();
                        }
                    }
                }

                return true;
            }

            return false;
        }

        private int CurrentlyInstalledVersion()
        {
            if (Directory.Exists(m_InstallPath))
            {
                string sptvExeFilePath = Path.GetFullPath(m_InstallPath + "/SPTraceView.exe");
                if (File.Exists(sptvExeFilePath))
                {
                    try
                    {
                        AssemblyName an = AssemblyName.GetAssemblyName(sptvExeFilePath);
                        Version sptvVer = an.Version;
                        return 10000 * sptvVer.Major + 1000 * sptvVer.Minor + 100 * sptvVer.Build + sptvVer.Revision;
                    }
                    catch
                    { }
                }
            }
            else
            {
                Directory.CreateDirectory(m_InstallPath);
            }

            return 0;
        }
    }
}
